#include "libcomm.h"

#ifdef COMM_MSVC
#include <ObjBase.h>
static HMODULE hAppModule = NULL;
#ifdef _USRDLL
BOOL APIENTRY DllMain( HMODULE hModule,
					  DWORD  ul_reason_for_call,
					  LPVOID lpReserved
					  )
{
	hAppModule = hModule;
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
	}
	return TRUE;
}
#endif 
#endif

#ifndef MAX_PATH
#define MAX_PATH 256
#endif

static void __GetApplicationFilePath(char dir[MAX_PATH],void* hmodule=NULL)
{
	MYLIB_ASSERT_NOTNULL(dir);
	int cnt = -1;
#ifdef COMM_MSVC
	cnt = ::GetModuleFileNameA(reinterpret_cast<HMODULE>(hmodule),dir,MAX_PATH-1);
	if(0 == cnt)
	{
		LOGFUNCERROR(GetModuleFileName);
	}
#else
	cnt = readlink("/proc/self/exe", dir, MAX_PATH-1);
	if (cnt < 0 || cnt >= MAX_PATH - 1)
	{
		LOGFUNCERROR(readlink);
		cnt = 0;
	}
#endif
	dir[cnt] = 0;
}


String Application::GetApplicationFilePath()
{
	String tmp(MAX_PATH);
	__GetApplicationFilePath(tmp);
	return (char*)tmp;
}

String Application::GetApplicationDirectory()
{
	return Application::GetPathDirectory(Application::GetApplicationFilePath());
}

String Application::GetApplicationFileName()
{
	return Application::GetPathFileName(Application::GetApplicationFilePath());
}

#ifdef COMM_MSVC

String Application::GetModuleFilePath()
{
	String tmp(MAX_PATH);
	__GetApplicationFilePath(tmp,(void*)hAppModule);
	return (char*)tmp;
}
String Application::GetModuleDirectoryPath()
{
	return Application::GetPathDirectory(Application::GetModuleFilePath());
}
String Application::GetModuleFileName()
{
	return Application::GetPathFileName(Application::GetModuleFilePath());
}

#include <ImageHlp.h>
#pragma comment(lib, "dbghelp.lib")
static LONG __ApplicationCrashHandler(EXCEPTION_POINTERS *pException)   
{ 
	LONG lResult = EXCEPTION_CONTINUE_SEARCH;  
	char szDumpFilePath[MAX_PATH] = {0};
	char szExeFilePath[MAX_PATH] = {0};
	__GetApplicationFilePath(szExeFilePath/*,(void*)hAppModule*/);
	SYSTEMTIME currentTime;
	GetLocalTime(&currentTime);
	_snprintf(szDumpFilePath,MAX_PATH-1,"%s_%d%02d%02d_%02d%02d%02d%03d.dmp",szExeFilePath,
		currentTime.wYear,currentTime.wMonth,currentTime.wDay,
		currentTime.wHour,currentTime.wMinute,currentTime.wSecond,currentTime.wMilliseconds);

	HANDLE hFile = ::CreateFileA(szDumpFilePath, GENERIC_WRITE,   
		FILE_SHARE_WRITE, NULL, CREATE_NEW,   
		FILE_ATTRIBUTE_NORMAL, NULL );  

	if(hFile != INVALID_HANDLE_VALUE)  
	{  

		MINIDUMP_EXCEPTION_INFORMATION exptInfo;  
		exptInfo.ThreadId = ::GetCurrentThreadId();  
		exptInfo.ExceptionPointers = pException;  

		BOOL bOK = ::MiniDumpWriteDump(::GetCurrentProcess(),  
			::GetCurrentProcessId(),   
			hFile, MiniDumpNormal,  
			&exptInfo, NULL, NULL);  
		if(bOK)  
			lResult = EXCEPTION_EXECUTE_HANDLER;  
	} 
	return lResult;  
} 

void Application::CatchDumpWhenCrash()
{
	SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)__ApplicationCrashHandler);  
}

#endif

String Application::GetPathDirectory(const char* path)
{
	String tmp = path;
	char* pos = NULL;
#ifdef COMM_MSVC
	pos = strrchr((char*)tmp, '\\');
#else
	pos = strrchr((char*)tmp, '/');
#endif
	if (NULL != pos)
		pos[0] = 0;
	return (char*)tmp;
}

String Application::GetPathFileName(const char* path)
{
	String tmp = path;
	const char* pos = NULL;
#ifdef COMM_MSVC
	pos = strrchr(path, '\\');
#else
	pos = strrchr(path, '/');
#endif
	if (NULL != pos)
		tmp = pos + 1;
	return tmp;
}



StringList Application::ParseCommandLine(const char* cmdline)
{
	// -a 123 file "456 789" tt
	//
	StringList list;
	String sCmdLine = cmdline;

	// 将引号中的空格替换成31
	{
		char* pos = (char*)sCmdLine;
		bool replace_space = false;
		while (pos[0] != 0)
		{
			if(pos[0] == '\"')
			{
				replace_space = !replace_space;
				pos[0] = 32;
			}
			else if(pos[0] == 32 && replace_space)
			{
				pos[0] = 31;
			}
			++pos;
		}
	}

	list = String::split(sCmdLine," ");

	// 还原引号中的空格
	for(int i=0;i<list.count();i++)
	{
		char* pos = list[i];
		while (pos[0] != 0)
		{
			if(pos[0] == 31)
				pos[0] = 32;
			++pos;
		}
	}

	return list;
}

String Application::GenerateUUID()
{
	char buf[37];
#ifdef COMM_MSVC
	GUID guid;
	CoCreateGuid(&guid);
	sprintf(buf,
		"%08X-%04X-%04x-%02X%02X-%02X%02X%02X%02X%02X%02X",
		guid.Data1,
		guid.Data2,
		guid.Data3,
		guid.Data4[0],guid.Data4[1],
		guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5],
		guid.Data4[6], guid.Data4[7]
	);
#else
	static unsigned int seed_base = 1;
	const char *c = "89ab";
	char *p = buf;
	int n;

	unsigned int seed = 0;
	{
		do
		{
			int fd = 0;
			fd = open("/dev/urandom",O_RDONLY);
			if(-1 == fd)
				break;
			read(fd,&seed,sizeof(seed));
			close(fd);
		}while(0);

		if(0 == seed)
		{
			LOGWARN("using time second for random seed.");
			seed = time(NULL);
			++seed_base;
			seed += seed_base;
		}
		srand(seed);
	}

	for( n = 0; n < 16; ++n )
	{
		int b = rand()%255;
		switch( n )
		{
		case 6:
			sprintf(p, "4%x", b%15 );
			break;
		case 8:
			sprintf(p, "%c%x", c[rand()%strlen(c)], b%15 );
			break;
		default:
			sprintf(p, "%02x", b);
			break;
		}

		p += 2;
		switch( n )
		{
		case 3:
		case 5:
		case 7:
		case 9:
			*p++ = '-';
			break;
		}
	}
	*p = 0;
#endif
	return buf;
}


bool Application::IsSingleProcess(const char* filename)
{
#ifdef COMM_MSVC

	String sGlobalName = String::format("Global\\%s",filename);
	HANDLE hEvent = CreateEventA( NULL, FALSE, FALSE, sGlobalName );
	if(NULL != hEvent && ERROR_ALREADY_EXISTS == GetLastError())
		return false;
	if(NULL == hEvent)
		LOGFUNCERROR(CreateEvent);
	return true;

#else

	struct flock fl;
	int fd = open(filename, O_RDWR | O_CREAT);
	if (-1 == fd)
	{
		LOGFUNCERROR(open);
		return true;
	}
	if(-1 == fchmod(fd,0666))
		LOGFUNCERROR(fchmod);

	fl.l_type = F_WRLCK;
	fl.l_start = 0;
	fl.l_whence = SEEK_SET;
	fl.l_len = 0;
	if(0 != fcntl(fd, F_SETLK, &fl))
	{
		if (errno == EACCES || errno == EAGAIN)
		{
			close(fd);
			return false;
		}

		LOGFUNCERROR(fcntl);
	}

	{
		String spid = String::format("%-8d",getpid());
		if(-1 == write(fd,(char*)spid,spid.length()))
			LOGFUNCERROR(write);
	}

	return true;

#endif
}

